<?php
/**
 *
 * ImageUpload.php
 *
 * 图片上传组件
 *
 * @author     limin<lmin@vip.deyi.com>
 * @since      2015/7/15 18:16
 * @copyright  deyi.com
 *
 * useage:
 *
 * 初始化源图像文件加载
 * 1、从post文件上传图片
 * $upload->initOriginImageFromFile($file);　
 * ２、从base64 decode后的二进制上传图片
 * $upload->initOriginImageFromString(base64_decode($string));
 *
 * 初始化上传保存路径
 * $upload->setSaveRootPath($rootPath)->setSaveRelativePath($fileSuffixPath);
 *
 * 超过最大宽度，等比缩小
 * $upload->scaleWidth(ForumConstant::ATTACHMENT_IMAGE_MAX_WIDTH);
 *
 * 保存之前给原图打水印
 * $upload->makeWaterMark($waterMark);
 *
 * 设置文件名并保存图片文件
 * $upload->setFileName($fileName)->save()
 *
 * +++++保存之后可以进行的操作
 *
 * 获取上传后的图片文件详细信息
 * $upload->getSavedFileInfo()
 *
 * 生成缩略图，可设置缩略图文件名后缀
 * $upload->makeNormalThumb(300, '_mthumb')->save();
 *
 * 中心点裁切正方体缩略图
 * $upload->makeCenterCropThumb(200, '_crop_center')->save();　
 *　
 */

namespace Deyi\Components;


class ImageUpload extends Upload
{
    /**
     * jpeg格式图片保存质量
     * @var int
     */
    private $_quality = 95;

    /**
     * jpeg格式图片是否保存为隔行扫描模式
     * @var bool
     */
    private $_interlace = false;

    /**
     * 原始图像资源
     * @var
     */
    private $_originImage;

    /**
     * 原始图像信息　
     * @var null　[‘width’ => 宽度值, 'height' => 高度值, 'ratio' => 图片比例, 'mime' => 图片类型]
     */
    private $_oImgOpt;

    /**
     * 工作图像资源
     * @var
     */
    private $_workImage;

    /**
     * 原始图像信息　
     * @var null　[‘width’ => 宽度值, 'height' => 高度值, 'ratio' => 图片比例, 'mime' => 图片类型]
     */
    private $_wImgOpt;


    /**
     * 上传方式, 分为文件和base64上传，两种方式
     * @var string file|string
     */
    protected $_uploadType = 'file';

    /**
     * string文件类型
     * @var
     */
    private $_stringMime;

    public function __construct()
    {
        parent::__construct();

    }

    public function __destruct()
    {
        if (is_resource($this->_originImage)) {
            imagedestroy($this->_originImage);
        }

        if (is_resource($this->_workImage)) {
            imagedestroy($this->_workImage);
        }
    }


    public function initOriginImageFromFile($file)
    {
        if (is_resource($this->_originImage)) {
            imagedestroy($this->_originImage);
            $this->_oImgOpt = null;
        }

        $this->setFile($file);

        if ($this->isImage()) {
            $this->_uploadType = 'file';
            $this->_loadOriginImageFile();

        }
    }

    public function initOriginImageFromString($string)
    {
        if (is_resource($this->_originImage)) {
            imagedestroy($this->_originImage);
            $this->_oImgOpt = null;
        }

        if ($string) {
            $this->_uploadType = 'string';
            $this->_loadOriginImageString($string);
        }
    }

    public function getOriginImageWidth()
    {
        return $this->_oImgOpt['width'];
    }

    public function getOriginImageHeight()
    {
        return $this->_oImgOpt['height'];
    }

    public function scaleWidth($width, $isOriginImage = true)
    {
        $scalePercent = 1;

        if ($width < $this->_oImgOpt['width']) {
            $scalePercent = round($width / $this->_oImgOpt['width'], 2);
            $height = ceil($this->_oImgOpt['height'] * $scalePercent);
            $this->_resize($width, $height, $isOriginImage);
        }

        return $this;
    }


    public function makeNormalThumb($width, $suffix = '')
    {
        $this->scaleWidth($width, false);
        $this->_fileSuffix = $suffix;

        return $this;
    }

    public function makeCenterCropThumb($width, $suffix = '')
    {
        $height = $width;

        if ($this->_oImgOpt['ratio'] > 1) {
            if ($height < $this->_oImgOpt['height']) {
                $scalePercent = round($this->_oImgOpt['height'] / $height, 2);
            }
        } else {
            if ($width < $this->_oImgOpt['width']) {
                $scalePercent = round($this->_oImgOpt['width'] / $width, 2);
            }
        }

        $scaleHeight = ceil($height * $scalePercent);
        $scaleWidth = ceil($width * $scalePercent);

        $centerPos = static::calcCenterPos($this->_oImgOpt['width'], $this->_oImgOpt['height']);
        $x1 = $centerPos['x'] - (round($scaleWidth / 2));
        $y1 = $centerPos['y'] - (round($scaleHeight / 2));
        $x2 = $x1 + $scaleWidth;
        $y2 = $y1 + $scaleHeight;
        $this->_crop($x1, $y1, $x2, $y2);

        $srcOpt['width'] = $width;
        $srcOpt['height'] = $height;
        static::_cloneImage($this->_workImage, $this->_workImage, $this->_wImgOpt, $srcOpt);

        $this->_fileSuffix = $suffix;

        return $this;
    }

    public function save()
    {
        $savePath = $this->getSaveAbsPath();
        $this->checkDirExsits($savePath);

        $destFile = $savePath . $this->getFileName();

        $result = false;

        if ($this->getFileExt() === 'gif') {
            $result = move_uploaded_file($this->getTmpFile(), $destFile);
        } else {
            $result = $this->printImage($this->_getSavedImage(), $destFile);
        }

        if ($result) {
            $this->_saveAbsFilename = $destFile;
            $this->_saveRelativeFilename = $this->getSaveRelativePath() . $this->getFileName();
        }

        return $result;
    }

    public function getSavedFileInfo()
    {
        $info['name'] = $this->_uploadType === 'file' ? $this->_file['name'] : $this->getFileName();
        $info['type'] = $this->getFileMime($this->_saveAbsFilename);
        $info['size'] = (int)filesize($this->_saveAbsFilename);
        $info['width'] = $this->_wImgOpt['width'] ? $this->_wImgOpt['width'] : $this->_oImgOpt['width'];
        $info['height'] = $this->_wImgOpt['height'] ? $this->_wImgOpt['height'] : $this->_oImgOpt['height'];
        $info['relative_filename'] = $this->_saveRelativeFilename;
        $info['absolute_filename'] = $this->_saveAbsFilename;
        $info['filename'] = $this->getFileName();

        $info['width'] = (int)$info['width'];
        $info['height'] = (int)$info['height'];

        return $info;
    }

    public function makeWaterMark($file, $position = 'right-bottom')
    {
        if (!is_file($file)) {
            throw new \Exception('水印文件不存在');
        }
        $logoType = $this->getFileMime($file);

        if ($logoType === 'image/png') {
            $waterImage = imagecreatefrompng($file);

            if (is_resource($waterImage)) {
                $width = imagesx($waterImage);
                $height = imagesy($waterImage);
            } else {
                throw new \Exception('水印文件无法解析');
            }
        } else {
            throw new \Exception('水印仅支持png格式');
        }

        if (!is_resource($this->_workImage)) {
            static::_cloneImage($this->_originImage, $this->_workImage, $this->_oImgOpt, $this->_wImgOpt);
        }

        $pos = static::calcWaterPos($position, $this->_wImgOpt['width'], $this->_wImgOpt['height'], $width, $height);

        if ($pos['x'] > 0 && $pos['y'] > 0) {
            imagecopyresampled(
                $this->_workImage,
                $waterImage,
                $pos['x'],
                $pos['y'],
                0,
                0,
                $width,
                $height,
                $width,
                $height
            );
        }

        return $this;
    }


    private function _loadOriginImageFile()
    {
        $file = $this->getTmpFile();

        switch ($this->getFileExt()) {
            case 'jpg':
                $this->_originImage = imagecreatefromjpeg($file);
                break;
            case 'png':
                $this->_originImage = imagecreatefrompng($file);
                break;
            case 'gif':
                $this->_originImage = imagecreatefromgif($file);
                break;
        }
        if (!is_resource($this->_originImage)) {
            throw new \RuntimeException('图片无法识别!', 301);
        }

        $this->_oImgOpt = static::_initImageOption($this->_originImage);

        return $this;
    }

    private function _loadOriginImageString($string)
    {
        $this->_originImage = imagecreatefromstring($string);

        if (is_resource($this->_originImage)) {
            $headerInfo = substr($string, 0, 100);

            //通过接收base64编码图片处理时，图片类型判断
            if (stripos($headerInfo, 'PNG') === 1) {
                $this->_fileExt = 'png';
            } elseif (stripos($headerInfo, 'GIF') === 0) {
                $this->_fileExt = 'gif';
            } else {
                $this->_fileExt = 'jpg';
            }
            $this->_oImgOpt = static::_initImageOption($this->_originImage);
        } else {
            throw new \RuntimeException('图片无法识别!', 301);
        }
    }

    private static function _initImageOption($imgResource)
    {
        $option = null;
        if (is_resource($imgResource)) {
            $option['width'] = imagesx($imgResource);
            $option['height'] = imagesy($imgResource);
            $option['ratio'] = round($option['width'] / $option['height'], 2);
        }

        return $option;
    }

    private static function _cloneImage($src, &$dest, $srcOpt, &$destOpt, $srcX = 0, $srcY = 0)
    {
        if (is_resource($src)) {
            $destOpt = isset($destOpt['width']) && isset($destOpt['height']) ? $destOpt : $srcOpt;

            $dest = imagecreatetruecolor($destOpt['width'], $destOpt['height']);
            imagealphablending($dest, true);
            $transparent = imagecolorallocatealpha($src, 255, 255, 255, 127);
            imagefill($dest, 0, 0, $transparent);

            imagecopyresampled($dest, $src, 0, 0, $srcX, $srcY, $destOpt['width'], $destOpt['height'], $srcOpt['width'], $srcOpt['height']);

        }

        return is_resource($dest) ? true : false;
    }

    private function _getSavedImage()
    {
        if (is_resource($this->_workImage)) {
            return $this->_workImage;
        }

        if (is_resource($this->_originImage)) {
            return $this->_originImage;
        }
    }

    private static function calcCenterPos($w, $h)
    {
        $pos = ['x' => 0, 'y' => 0];
        $pos['x'] = ceil($w / 2);
        $pos['y'] = ceil($h / 2);

        return $pos;
    }

    private static function calcWaterPos($position, $imageWidth, $imageHeight, $waterWidth, $waterHeight)
    {
        $x = $y = 0;
        switch ($position) {
            case 'right-bottom':
                $x = $imageWidth - $waterWidth;
                $y = $imageHeight - $waterHeight;
                break;
        }

        return ['x' => $x, 'y' => $y];
    }

    private function _resize($width, $height, $isOriginImage)
    {
        $this->_wImgOpt['width'] = $width;
        $this->_wImgOpt['height'] = $height;

        if ($isOriginImage) {
            static::_cloneImage($this->_originImage, $this->_originImage, $this->_oImgOpt, $this->_wImgOpt);
        } else {
            static::_cloneImage($this->_originImage, $this->_workImage, $this->_oImgOpt, $this->_wImgOpt);
        }

        return $this;
    }

    private function _crop($x1, $y1, $x2, $y2)
    {
        $x1 = $x1 < 0 ? 0 : $x1;
        $y1 = $y1 < 0 ? 0 : $y1;

        if (($x1 >= $x2) || ($y1 >= $y2)) {
            throw new \Exception('图像裁切点x2必须大于x1, y2必须大于y1');
        }

        $srcOpt['width'] = $x2 - $x1;
        $srcOpt['height'] = $y2 - $y1;
        static::_cloneImage($this->_originImage, $this->_workImage, $srcOpt, $this->_wImgOpt, $x1, $y1);

        return $this;

    }

    private function printImage($imgObj, $filename = null)
    {
        $type = $this->getFileExt();
        $status = false;
        if ($filename === null) {
            header('Content-type: image/' . $type);
        }

        switch ($type) {
            case 'jpg':
                imageinterlace($imgObj, $this->_interlace);
                $status = imagejpeg($imgObj, $filename, $this->_quality);
                break;
            case 'png':
                imagesavealpha($imgObj, true);
                $status = imagepng($imgObj, $filename, 2);
                break;
            case 'gif':
                $status = imagegif($imgObj, $filename);
                break;
        }

        if ($filename === null) {
            exit;
        } else {
            return $status;
        }
    }


}
